package com.example.handler;

import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.example.untils.ExceptionUtil;
import com.example.untils.IPUtils;
import com.example.untils.annotation.AopLog;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 全局日志拦截器
 * 
 * @author lli
 *
 * @version 1.0
 *
 */
@Aspect
@Component
public class LogAopHandler {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final ObjectMapper om = new ObjectMapper();

    @Pointcut("execution(* com.example.controllor..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")
    public void controllerMethodPointcut() {
    }

    @Around("controllerMethodPointcut()")
    public Object Interceptor(ProceedingJoinPoint pjp) throws Throwable {
        Object result = null;
        String strMethodName = pjp.getSignature().getName();
        String strClassName = pjp.getTarget().getClass().getSimpleName();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        AopLog log = methodSignature.getMethod().getAnnotation(AopLog.class);
        System.out.println(log.operation());
        /** 不添加日志 提前退出 */
        if (!isAddLog(strClassName, strMethodName)) {
            try {
                result = pjp.proceed();
            } catch (Exception e) {
                throw e;
            }
            return result;
        }
        /** 日志记录 */

        String ip = IPUtils.getRemoteAddr();

        Set<Object> allParams = new LinkedHashSet<>(); // 保存所有请求参数，用于输出到日志中
        Object[] args = pjp.getArgs();
        for (Object arg : args) {
            if (arg instanceof Map<?, ?>) {
                // 提取方法中的MAP参数，用于记录进日志中
                @SuppressWarnings("unchecked")
                Map<String, Object> map = (Map<String, Object>) arg;

                allParams.add(map);
            } else if (arg instanceof HttpServletRequest) {
                HttpServletRequest request = (HttpServletRequest) arg;

                // 获取query string 或 posted form data参数
                Map<String, String[]> paramMap = request.getParameterMap();
                if (paramMap != null && paramMap.size() > 0) {
                    allParams.add(paramMap);
                }
            } else {
                allParams.add(arg);
            }
        }
        long beginTime = System.currentTimeMillis();
        try {
            result = pjp.proceed();
        } catch (BusinessErrorException e) {

            logger.info("IP:{} 业务异常-异常编码：{}，异常提示信息：{}", ip,
                    ((BusinessErrorException) e).getCode(), e.getMessage());
            // 抛给 统计异常处理，但是也可以在这里做，demo作用
            throw e;

        } catch (Throwable e) {
            logger.error("IP:{} 服务异常: {}", ip,
                    ExceptionUtil.getExceptionAllinformation(e));
            throw e;
        } finally {
            long costMs = System.currentTimeMillis() - beginTime;
            logger.info("IP:{} {}.{}请求结束，耗时：{}ms，参数：{}，结果：{}", ip,
                    strClassName, strMethodName, costMs,
                    om.writeValueAsString(allParams),
                    om.writeValueAsString(result));

        }
        return result;
    }

    /**
     * 
     * @description 判断是否添加日志
     * @author lli
     * @create 2017年5月18日下午8:43:00
     * @version 1.0
     * @param className
     * @param methodName
     * @return
     */
    private boolean isAddLog(final String className, final String methodName) {
        if ("DemoControllor".equalsIgnoreCase(className)) {
            return false;
        }
        if ("Demo2Controllor".equalsIgnoreCase(className)
                && "stest".equalsIgnoreCase(methodName)) {
            return false;
        }
        return true;

    }

}